package com.zerui.manager.accessauthorization.gatewayimpl;


import com.alibaba.cola.exception.BizException;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dahuatech.icc.exception.ClientException;
import com.zerui.basedata.client.PersonnelService;
import com.zerui.basedata.client.dto.PersonnelDTO;
import com.zerui.cola.dto.MultiResponse;
import com.zerui.cola.dto.Response;
import com.zerui.manager.access.auth.task.domain.AuthTaskDetail;
import com.zerui.manager.accessauthorization.AccessAuth;
import com.zerui.manager.accessauthorization.convertor.AccessAuthConvert;
import com.zerui.manager.accessauthorization.entity.AccessAuthDo;
import com.zerui.manager.accessauthorization.gateway.AccessAuthGateWay;
import com.zerui.manager.accessauthorization.mapper.AccessAuthMapper;
import com.zerui.manager.feign.handler.DeviceFeignHandler;
import com.zerui.manager.feign.request.DeviceActionExecuteRequest;
import com.zerui.manager.icc.dto.PersonAuthorityVO;
import com.zerui.manager.icc.service.AccessControlInfoService;
import com.zerui.manager.icc.service.DeviceInfoService;
import com.zerui.manager.web.AccessAuthDeviceService;
import com.zerui.manager.web.cmd.AccessAuthQryCmd;
import com.zerui.manager.web.vo.AccessAuthDeviceVO;
import com.zerui.manager.web.vo.icc.DeviceInfoVO;
import com.zerui.manager.web.vo.icc.UnitVO;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author wh
 * @create_time 2023-08-23 15:28
 */
@Slf4j
@Component
public class AccessAuthGateWayImpl implements AccessAuthGateWay {

    @Resource
    private AccessAuthMapper accessAuthMapper;
	@Resource
    private DeviceFeignHandler deviceFeignHandler;
	@DubboReference
	private PersonnelService personnelService;
	@Resource
	private AccessAuthDeviceService accessAuthDeviceService;
	@Resource
	private DeviceInfoService deviceInfoService;
	@Resource
	private AccessControlInfoService accessControlInfoService;

	@Override
	public Response add(AccessAuth accessAuth) {
		AccessAuthDo accessAuthDo = AccessAuthConvert.INSTANCE.convertAccessAuthToAccessAuthDo(accessAuth);
		accessAuthMapper.insert(accessAuthDo);
		return Response.buildSuccess();
	}

	@Override
	public Response update(AccessAuth accessAuth) {
		AccessAuthDo accessAuthDo = AccessAuthConvert.INSTANCE.convertAccessAuthToAccessAuthDo(accessAuth);
		accessAuthMapper.updateById(accessAuthDo);
		return Response.buildSuccess();
	}

	@Override
	public AccessAuth selectById(Long id) {
		AccessAuthDo accessAuthDo = accessAuthMapper.selectById(id);
		return AccessAuthConvert.INSTANCE.convertAccessAuthDoToAccessAuth(accessAuthDo);
	}

	@Override
	public Response deleteById(Long id) {
		accessAuthMapper.deleteById(id);
		return Response.buildSuccess();
	}

	@Override
	public Page<AccessAuth> getPageList(AccessAuthQryCmd accessAuthQryCmd) {
		LambdaQueryWrapper<AccessAuthDo> queryWrapper = Wrappers.lambdaQuery();
		queryWrapper.eq(ObjectUtils.isNotEmpty(accessAuthQryCmd.getName()),AccessAuthDo::getName,accessAuthQryCmd.getName());
		queryWrapper.eq(ObjectUtils.isNotEmpty(accessAuthQryCmd.getDateType()),AccessAuthDo::getDateType,accessAuthQryCmd.getDateType());
		queryWrapper.eq(ObjectUtils.isNotEmpty(accessAuthQryCmd.getTimeType()),AccessAuthDo::getTimeType,accessAuthQryCmd.getTimeType());
		Page<AccessAuthDo> page = new Page<>(accessAuthQryCmd.getPageIndex(),accessAuthQryCmd.getPageSize());
		Page<AccessAuthDo> selectPage = accessAuthMapper.selectPage(page, queryWrapper);
		return AccessAuthConvert.INSTANCE.convertAccessAuthDoToAccessAuthPage(selectPage);
	}

	@Override
	public String addDistribute(List<Long> deviceIds, Set<Long> finalPersonnelList, Long accessAuthId) {
		AccessAuthDo accessAuthDo = accessAuthMapper.selectById(accessAuthId);
		//如果是日期范围
		Map<String, String> timeMap = computingTime(accessAuthDo);
		if(null == timeMap){
			return "";
		}
		return deviceFeignHandler.batchActionExecute(createDeviceActionExecuteRequest(deviceIds,finalPersonnelList,timeMap,"insertPersons","persons"));
	}

	@Override
	public String delDistribute(List<Long> finalDeviceIds, Set<Long> finalPersonnelList) {
		return deviceFeignHandler.batchActionExecute(createDeviceActionExecuteRequest(finalDeviceIds,finalPersonnelList,null,"removePersons","persons"));
	}

	@Override
	public List<AccessAuth> selectAll() {
		LambdaQueryWrapper<AccessAuthDo> queryWrapper = Wrappers.lambdaQuery();
		List<AccessAuthDo> accessAuthDos = accessAuthMapper.selectList(queryWrapper);
		return AccessAuthConvert.INSTANCE.convertAccessAuthDosToAccessAuths(accessAuthDos);
	}

	@Override
	public String addIoTAuthority(AuthTaskDetail iot) {
		return this.addDistribute(Collections.singletonList(iot.getDeviceId()), Collections.singleton(iot.getPersonnelId()), iot.getAccessAuthId());
	}

	@Override
	public String delIoTAuthority(AuthTaskDetail iot) {
		return this.delDistribute(Collections.singletonList(iot.getDeviceId()), Collections.singleton(iot.getPersonnelId()));
	}

	@Override
	public void addICCAuthority(AuthTaskDetail icc, String personCode) {
		AccessAuthDo accessAuthDo = accessAuthMapper.selectById(icc.getAccessAuthId()); // 门禁授权
		Map<String, String> timeMap = computingTime(accessAuthDo);
		if (null == timeMap) return; // 如果未指定日期，权限就不用下发
		Set<String> channelCodesSet = new HashSet<>(); // 通道信息
		AccessAuthDeviceVO accessAuthDevice = accessAuthDeviceService.selectByDeviceId(icc.getDeviceId());// 获取授权的门禁设备
		try {
			PersonAuthorityVO personAuthorityVO = accessControlInfoService.selectByCode(personCode);
			log.info("人员的授权状态 -> {}", JSONObject.toJSONString(personAuthorityVO));
			if (null != personAuthorityVO.getAuthorizeStatus() && personAuthorityVO.getAuthorizeStatus().equals("2") // 2：已授权
					&& personAuthorityVO.getPersonPrivilegeDetails().stream()
					.anyMatch(c -> "1".equals(c.getAuthorizeSource()))) { // 必须存在按人员授权信息 1：按人员授权
				log.info("该人员[{}]已授权,本次授权无效.", personCode);
				return; // 如果是【已授权】就不再做授权操作
			}
			DeviceInfoVO iccDeviceInfo = deviceInfoService.selectByDeviceCode(accessAuthDevice.getDeviceCode());
			for (UnitVO uint : iccDeviceInfo.getUnits()) {
				if (7 == uint.getUnitType()) // 单位类型为 7 才能下发门禁
					uint.getChannels().stream().filter(c -> StringUtils.isNotBlank(c.getChannelCode())).forEach(c -> channelCodesSet.add(c.getChannelCode()));
			}
		} catch (ClientException e) {
			throw new BizException("查询ICC设备信息出错", e);
		}
		log.info("要下发ICC的设备号 -> {} 通道 -> {}", accessAuthDevice.getDeviceCode(), String.join(",", channelCodesSet));
		try {
			accessControlInfoService.batchAuthority(Collections.singletonList(personCode), new ArrayList<>(channelCodesSet), timeMap.get("beginTime"), timeMap.get("endTime"));
		} catch (ClientException e) {
			throw new BizException("下发ICC门禁权限出错", e);
		}
	}

	@Override
	public void delICCAuthority(AuthTaskDetail icc, String personCode) {
		Set<String> channelCodesSet = new HashSet<>(); // 通道信息
		AccessAuthDeviceVO accessAuthDevice = accessAuthDeviceService.selectByDeviceId(icc.getDeviceId());// 获取授权的门禁设备
		try {
			PersonAuthorityVO personAuthorityVO = accessControlInfoService.selectByCode(personCode);
			if (null != personAuthorityVO.getAuthorizeStatus() && (personAuthorityVO.getAuthorizeStatus().equals("1") // 1：未授权
					|| personAuthorityVO.getPersonPrivilegeDetails().stream()
					.noneMatch(c -> "1".equals(c.getAuthorizeSource())))) { // 如果没有人员授权 1：人员授权
				log.info("该人员[{}]未授权,本次取消授权无效.", personCode);
				return;
			}
			DeviceInfoVO iccDeviceInfo = deviceInfoService.selectByDeviceCode(accessAuthDevice.getDeviceCode());
			for (UnitVO uint : iccDeviceInfo.getUnits()) {
				if (7 == uint.getUnitType()) // 单位类型为 7 才能下发门禁
					uint.getChannels().stream().filter(c -> StringUtils.isNotBlank(c.getChannelCode())).forEach(c -> channelCodesSet.add(c.getChannelCode()));
			}
		} catch (ClientException e) {
			throw new BizException("查询ICC设备信息出错", e);
		}
		try {
			accessControlInfoService.singleDelete(personCode, new ArrayList<>(channelCodesSet));
		} catch (ClientException e) {
			throw new BizException("删除ICC门禁权限出错", e);
		}
	}

	private List<DeviceActionExecuteRequest> createDeviceActionExecuteRequest(List<Long> deviceIds, Set<Long> personals, Map<String, String> timeMap,String command,String key) {
		String beginTime = "";
		String endTime = "";
		if(CollectionUtils.isNotEmpty(timeMap)){
			beginTime = timeMap.get("beginTime");
			endTime = timeMap.get("endTime");
		}
		List<DeviceActionExecuteRequest> requests = new ArrayList<>();
		String finalBeginTime = beginTime;
		String finalEndTime = endTime;
		deviceIds.forEach(deviceId -> requests.add(
				DeviceActionExecuteRequest.builder()
						.id(deviceId.toString())
						.beginTime(finalBeginTime)
						.endTime(finalEndTime)
						.actions(createAction(command,personals,key))
						.build()
		));
		return requests;
	}

	private Map<String, Map<String, Object>> createAction(String action,Set<Long> personals,String key) {
		Map<String, Map<String, Object>> map = new HashMap<>();
		if(action.equals("insertPersons")){
			map.put(action,createPersonsKeyMap(key,personals));
		}else {
			map.put(action,createKeyMap(key,personals));
		}
		return map;
	}

	private Map<String, Object> createKeyMap(String key,Set<Long> personals) {
		Map<String, Object> map = new HashMap<>();
		Set<String> personalsStr = personals.stream()
				.map(e->e.toString())
				.collect(Collectors.toSet());
		map.put(key, personalsStr);
		return map;
	}

	private Map<String, Object> createPersonsKeyMap(String key,Set<Long> personals) {
		Map<String, Object> map = new HashMap<>();
		//根据personals 查询人员信息
		List<PersonNode> nodes = new ArrayList<>();
		MultiResponse<PersonnelDTO> response = personnelService.getBatchPersonnel(personals.stream().collect(Collectors.toList()));
		List<PersonnelDTO> data = response.getData();
		Map<Long, String> icCodeMap = new HashMap<>();
		if(CollectionUtils.isNotEmpty(data)){
			Map<Long, String> finalIcCodeMap = icCodeMap;
			data.forEach(e->{
				nodes.add(
						PersonNode.builder()
						.personId(e.getId().toString())
						.faceImage(e.getFaceImgUrl() == null ? "":e.getFaceImgUrl())
						.gender(StringUtils.isBlank(e.getGender()) ? "UNKNOWN" : (e.getGender().equals("男") ? "MALE" : "FEMALE"))
						.personName(StringUtils.isBlank(e.getName()) ? "":e.getName())
								.ICCardID(StringUtils.isBlank(e.getIccCardNum()) ? "" : e.getIccCardNum())
						.qrCode(StringUtils.isBlank(finalIcCodeMap.get(e.getId())) ? "" : finalIcCodeMap.get(e.getId()))
						.build());
			});
		}
		map.put(key, nodes);
		return map;
	}

	private Map<String,String> computingTime(AccessAuthDo accessAuthDo) {
		if (accessAuthDo.getDateType().equals(0) && accessAuthDo.getTimeType().equals(0)) {
			return new HashMap<>();
		}
		LocalDate now_Date = LocalDate.now();
		LocalDateTime beginTime = null;
		LocalDateTime endTime = null;

		if (accessAuthDo.getDateType().equals(1)) { // 有日期
			//判断当前时间是否在时间范围内
			LocalDate start_Date = accessAuthDo.getStartDate();
			LocalDate end_Date = accessAuthDo.getEndDate();

			if (now_Date.isBefore(start_Date) || now_Date.isAfter(end_Date)) {
				return null;
			}

			if (accessAuthDo.getTimeType().equals(0)) {
				beginTime = start_Date.atTime(0, 0, 0);
				endTime = end_Date.atTime(0, 0, 0);
			} else if (accessAuthDo.getTimeType().equals(1)) {
				LocalTime start_Time = accessAuthDo.getStartTime();
				LocalTime end_Time = accessAuthDo.getEndTime();
				beginTime = start_Time.atDate(start_Date);
				endTime = end_Time.atDate(end_Date);
			}
		} else if (accessAuthDo.getTimeType().equals(1)) {
			LocalTime start_Time = accessAuthDo.getStartTime();
			LocalTime end_Time = accessAuthDo.getEndTime();
			LocalTime now_Time = LocalTime.now();

			if (now_Time.isAfter(end_Time)) {
				return null;
			}

			beginTime = start_Time.atDate(now_Date);
			endTime = end_Time.atDate(now_Date);
		}
		if (ObjectUtils.isEmpty(beginTime) || ObjectUtils.isEmpty(endTime)) {
			return null;
		}
		Map<String, String> map = new HashMap<>();
		map.put("beginTime", beginTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
		map.put("endTime", endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
		return map;
	}

	public static void main(String[] args) {
		LocalDateTime beginTime = LocalDate.now().atTime(0,0,0);
		LocalDateTime endTime = LocalDate.now().plusDays(1).atTime(0,0,0);
		System.out.println(beginTime);
		System.out.println(endTime);
	}

	@Getter
	@Setter
	@ToString
	@Builder
	@NoArgsConstructor
	@AllArgsConstructor
	public static class PersonNode{

		private String personId;
		private String personName;
		private String qrCode;
		private String faceImage;
		private String gender;
		private String ICCardID;
	}
}
